/**
 * 
 */
package com.google.code.netz.wfe.model;

import static com.google.code.netz.wfe.events.PlaceEvent.PlaceEventType.TOKEN_ADDED;
import static com.google.code.netz.wfe.events.PlaceEvent.PlaceEventType.TOKEN_DELETED;

import java.util.ArrayList;
import java.util.List;

import com.google.code.netz.wfe.events.EventManager;
import com.google.code.netz.wfe.events.PlaceEvent;

/**
 * Place is an entity that contributes towards the state of a ICase in
 * conjunction with a {@link IToken}. {@link Place} models a Condition.
 * 
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @since 1.0
 * @version $Id$
 */
public class Place<D> extends AbstractPetriNetEntity<D> {

	/**
	 * Tokens put as a result of firing {@link Transition}(s) that connect to
	 * this {@link Place}.
	 */
	private List<IToken<?>> tokens;

	/**
	 * Number of tokens that this {@link Place} can hold.
	 * <p>
	 * A value of <i>Zero</i> implies no limit.
	 */
	private int threshold;

	/**
	 * {@link InputArc}s that connect to this Place.
	 */
	private List<InputArc<?>> inputs;

	/**
	 * {@link OutputArc}s from this Place.
	 */
	private List<OutputArc<?>> outputs;

	/**
	 * Creates a new Place with a unlimited threshold for {@link IToken}s
	 * allowed.
	 * 
	 * @param label
	 */
	public Place(String label) {
		this(label, 0); // threshold '0' implies unlimited Tokens.
	}

	/**
	 * 
	 * @param label
	 * @param threshold
	 *            maximum number of Tokens this Place can hold.
	 */
	public Place(String label, int threshold) {
		super(label);
		this.threshold = threshold;
	}

	/**
	 * Returns list of all {@link IToken}s put on this Place.
	 * 
	 * @return
	 */
	public List<IToken<?>> getTokens() {
		return this.tokens;
	}

	/**
	 * Adds the specified {@link IToken} to this Place.
	 * 
	 * @param t
	 */
	public void addToken(IToken<?> t) {
		if (threshold > 0 && this.tokens.size() == threshold) {
			// TODO: Throw a Threshold exceeded exception
			// A place can only have a certain number of tokens.
		}

		if (null == this.tokens)
			this.tokens = new ArrayList<IToken<?>>();
		this.tokens.add(t);

		EventManager.getInstance().dispatchEvent(new PlaceEvent<Place<?>>(this, TOKEN_ADDED));
	}

	/**
	 * Removes the specified {@link IToken} off this Place.
	 * 
	 * @param t
	 */
	public void removeToken(IToken<?> t) {
		if (null != this.tokens && this.tokens.contains(t)) {
			this.tokens.remove(t);
			EventManager.getInstance().dispatchEvent(new PlaceEvent<Place<?>>(this, TOKEN_DELETED));
		}
		// else ignore silently
	}

	/**
	 * Max number of {@link IToken}s that this Place instance can hold.
	 * 
	 * @return
	 */
	public int getThreshold() {
		return this.threshold;
	}

	/**
	 * 
	 * @param p
	 */
	public void addInput(InputArc<?> i) {
		if (null == inputs)
			this.inputs = new ArrayList<InputArc<?>>();
		this.inputs.add(i);
	}

	/**
	 * 
	 * @param p
	 */
	public void addOutput(OutputArc<?> o) {
		if (null == outputs)
			this.outputs = new ArrayList<OutputArc<?>>();
		this.outputs.add(o);
	}

	/**
	 * Removes the specified {@link InputArc} connection to this Transition.
	 * 
	 * @param i
	 */
	public void removeInput(InputArc<?> i) {
		if (null != inputs && this.inputs.contains(i))
			this.inputs.remove(i);
	}

	/**
	 * Removes the specified {@link OutputArc} connection from this Transition.
	 * 
	 * @param o
	 */
	public void removeInput(OutputArc<?> o) {
		if (null != outputs && this.outputs.contains(o))
			this.outputs.remove(o);
	}

	/**
	 * @return the inputs
	 */
	public List<InputArc<?>> getInputs() {
		return inputs;
	}

	/**
	 * @return the outputs
	 */
	public List<OutputArc<?>> getOutputs() {
		return outputs;
	}
}
